001 /*
002 * Copyright 2003-2005 The Apache Software Foundation
003 * Copyright 2005 Stephen McConnell
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package net.dpml.cli.validation;
018
019 import java.text.DateFormat;
020 import java.text.ParsePosition;
021
022 import java.util.Date;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.ListIterator;
026
027 import net.dpml.cli.resource.ResourceConstants;
028 import net.dpml.cli.resource.ResourceHelper;
029
030 /**
031 * The <code>DateValidator</code> validates the argument values
032 * are date or time value(s).
033 *
034 * The following example shows how to validate that
035 * an argument value(s) is a Date of the following
036 * type: d/M/yy (see {@link java.text.DateFormat}).
037 *
038 * <pre>
039 * DateFormat date = new SimpleDateFormat("d/M/yy");
040 * ...
041 * ArgumentBuilder builder = new ArgumentBuilder();
042 * Argument dateFormat =
043 * builder.withName("date");
044 * .withValidator(new DateValidator(dateFormat));
045 * </pre>
046 *
047 * The following example shows how to validate that
048 * an argument value(s) is a time of the following
049 * type: HH:mm:ss (see {@link java.text.DateFormat}).
050 *
051 * <pre>
052 * DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
053 * ...
054 * ArgumentBuilder builder = new ArgumentBuilder();
055 * Argument time =
056 * builder.withName("time");
057 * .withValidator(new DateValidator(timeFormat));
058 * </pre>
059 *
060 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
061 * @version 1.0.0
062 * @see java.text.DateFormat
063 */
064 public class DateValidator implements Validator
065 {
066 /** i18n */
067 private static final ResourceHelper RESOURCES =
068 ResourceHelper.getResourceHelper();
069
070 /** an array of permitted DateFormats */
071 private DateFormat[] m_formats;
072
073 /** minimum Date allowed i.e: a valid date occurs later than this date */
074 private Date m_minimum;
075
076 /** maximum Date allowed i.e: a valid date occurs earlier than this date */
077 private Date m_maximum;
078
079 /** leniant parsing */
080 private boolean m_isLenient;
081
082 /**
083 * Creates a Validator for the default date/time format
084 */
085 public DateValidator()
086 {
087 this( DateFormat.getInstance() );
088 }
089
090 /**
091 * Creates a Validator for the specified DateFormat.
092 *
093 * @param format
094 * a DateFormat which dates must conform to
095 */
096 public DateValidator( final DateFormat format )
097 {
098 setFormat( format );
099 }
100
101 /**
102 * Creates a Validator for the List of specified DateFormats.
103 *
104 * @param formats a List of DateFormats which dates must conform to
105 */
106 public DateValidator( final List formats )
107 {
108 for( Iterator iter = formats.iterator(); iter.hasNext();)
109 {
110 DateFormat format = (DateFormat) iter.next();
111 }
112 setFormats( formats );
113 }
114
115 /**
116 * Creates a Validator for dates.
117 *
118 * @return DateValidator a Validator for dates
119 */
120 public static DateValidator getDateInstance()
121 {
122 return new DateValidator( DateFormat.getDateInstance() );
123 }
124
125 /**
126 * Creates a Validator for times.
127 *
128 * @return DateValidator a Validator for times
129 */
130 public static DateValidator getTimeInstance()
131 {
132 return new DateValidator( DateFormat.getTimeInstance() );
133 }
134
135 /**
136 * Creates a Validator for date/times
137 *
138 * @return DateValidator a Validator for date/times
139 */
140 public static DateValidator getDateTimeInstance()
141 {
142 return new DateValidator( DateFormat.getDateTimeInstance() );
143 }
144
145 /**
146 * Validate each String value in the specified List against this instances
147 * permitted DateFormats.
148 *
149 * If a value is valid then it's <code>String</code> value in the list is
150 * replaced with it's <code>Date</code> value.
151 *
152 * @param values the list of values to validate
153 * @exception InvalidArgumentException if a value is invalid
154 * @see net.dpml.cli.validation.Validator#validate(java.util.List)
155 */
156 public void validate( final List values ) throws InvalidArgumentException
157 {
158 // for each value
159 for( final ListIterator i = values.listIterator(); i.hasNext();)
160 {
161 final Object next = i.next();
162 if( next instanceof Date )
163 {
164 return;
165 }
166
167 final String value = (String) next;
168
169 Date date = null;
170
171 // create a resuable ParsePosition instance
172 final ParsePosition pp = new ParsePosition( 0 );
173
174 // for each permitted DateFormat
175 for( int f=0; ( f<m_formats.length ) && ( date == null ); ++f )
176 {
177 // reset the parse position
178 pp.setIndex( 0 );
179 date = m_formats[f].parse( value, pp );
180
181 // if the wrong number of characters have been parsed
182 if( pp.getIndex() < value.length() )
183 {
184 date = null;
185 }
186 }
187
188 // if date has not been set throw an InvalidArgumentException
189 if( date == null )
190 {
191 throw new InvalidArgumentException( value );
192 }
193
194 // if the date is outside the bounds
195 if( isDateEarlier( date ) || isDateLater( date ) )
196 {
197 throw new InvalidArgumentException(
198 RESOURCES.getMessage(
199 ResourceConstants.DATEVALIDATOR_DATE_OUTOFRANGE,
200 value ) );
201 }
202
203 // replace the value in the list with the actual Date
204 i.set( date );
205 }
206 }
207
208 /**
209 * Set the leaniant flag.
210 * @param lenient true if leniant
211 */
212 public void setLeniant( final boolean lenient )
213 {
214 for( int i=0; i<m_formats.length; i++ )
215 {
216 m_formats[i].setLenient( lenient );
217 }
218 m_isLenient = lenient;
219 }
220
221 /**
222 * Return the leaniant flag.
223 * @return true if leniant
224 */
225 public boolean isLeniant()
226 {
227 return m_isLenient;
228 }
229
230 /**
231 * Returns the maximum date permitted.
232 *
233 * @return Date the maximum date permitted. If no maximum date has been
234 * specified then return <code>null</code>.
235 */
236 public Date getMaximum()
237 {
238 return m_maximum;
239 }
240
241 /**
242 * Sets the maximum Date to the specified value.
243 *
244 * @param maximum
245 * the maximum Date permitted
246 */
247 public void setMaximum( final Date maximum )
248 {
249 m_maximum = maximum;
250 }
251
252 /**
253 * Returns the minimum date permitted.
254 *
255 * @return Date the minimum date permitted. If no minimum date has been
256 * specified then return <code>null</code>.
257 */
258 public Date getMinimum()
259 {
260 return m_minimum;
261 }
262
263 /**
264 * Sets the minimum Date to the specified value.
265 *
266 * @param minimum
267 * the minimum Date permitted
268 */
269 public void setMinimum( Date minimum )
270 {
271 m_minimum = minimum;
272 }
273
274 /**
275 * Returns whether the specified Date is later than the maximum date.
276 *
277 * @param date
278 * the Date to evaluate
279 *
280 * @return boolean whether <code>date</code> is earlier than the maximum
281 * date
282 */
283 private boolean isDateLater( Date date )
284 {
285 return ( m_maximum != null ) && ( date.getTime() > m_maximum.getTime() );
286 }
287
288 /**
289 * Returns whether the specified Date is earlier than the minimum date.
290 *
291 * @param date the Date to evaluate
292 * @return boolean whether <code>date</code> is earlier than the minimum
293 * date
294 */
295 private boolean isDateEarlier( Date date )
296 {
297 return ( m_minimum != null ) && ( date.getTime() < m_minimum.getTime() );
298 }
299
300 /**
301 * Sets the date format permitted.
302 *
303 * @param format
304 * the format to use
305 */
306 public void setFormat( final DateFormat format )
307 {
308 setFormats( new DateFormat[]{format} );
309 }
310
311 /**
312 * Sets the date formats permitted.
313 *
314 * @param formats
315 * the List of DateFormats to use
316 */
317 public void setFormats( final List formats )
318 {
319 setFormats( (DateFormat[]) formats.toArray( new DateFormat[formats.size()] ) );
320 }
321
322 /**
323 * Sets the date formats permitted.
324 *
325 * @param formats the array of DateFormats to use
326 */
327 public void setFormats( final DateFormat[] formats )
328 {
329 m_formats = formats;
330 setLeniant( m_isLenient );
331 }
332
333 /**
334 * Gets the date formats permitted.
335 *
336 * @return the permitted formats
337 */
338 public DateFormat[] getFormats()
339 {
340 return m_formats;
341 }
342 }